package com.authine.cloudpivot.engine.service.utils.sftp;

import com.alibaba.fastjson.JSON;
import com.authine.cloudpivot.engine.api.model.system.SystemSettingModel;
import com.authine.cloudpivot.engine.enums.type.FileUploadType;
import com.authine.cloudpivot.engine.enums.type.SettingType;
import com.authine.cloudpivot.web.api.dubbo.DubboConfigService;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Author hxd
 * @Date 2021/12/17 15:37
 * @Description
 **/
@Component
public class CustSFTP {


    private static final Logger log = LoggerFactory.getLogger(CustSFTP.class);
    private static final String MIMETYPE_SUFFIX = "application/";
    private String ftpPath;
    private volatile boolean isConfig = false;
    @Autowired
    private DubboConfigService dubboConfigService;
    @Value("${cloudpivot.sftp.pubkeyAuthentication:false}")
    private boolean pubkeyAuthentication;
    @Value("${cloudpivot.sftp.idRsaPath:/root/.ssh/id_rsa}")
    private String idRsaPath;
    private ChannelSftpPool channelSftpPool;

    public CustSFTP() {
    }

    private void initSFTPConfig() {
        this.log();
        if (!this.isConfig) {
            this.isConfig = true;
            List<SystemSettingModel> systemSettings = this.dubboConfigService.getSystemManagementFacade().getSystemSettingsBySettingType(SettingType.FILE_UPLOAD);
            Map<String, SystemSettingModel> settingMap = (Map) systemSettings.stream().filter((t) -> {
                return FileUploadType.FTP.equals(t.getFileUploadType()) && t.getChecked();
            }).collect(Collectors.toMap(SystemSettingModel::getSettingCode, Function.identity()));
            if (log.isDebugEnabled()) {
                log.debug("加载SFTP配置：{}", JSON.toJSONString(settingMap));
            }

            int ftpPort = Integer.parseInt(((SystemSettingModel) settingMap.get("ftpPort")).getSettingValue());
            String ftpServer = ((SystemSettingModel) settingMap.get("ftpServer")).getSettingValue();
            String ftpAccount = ((SystemSettingModel) settingMap.get("ftpAccount")).getSettingValue();
            String ftpPassword = ((SystemSettingModel) settingMap.get("ftpPassword")).getSettingValue();
            this.ftpPath = ((SystemSettingModel) settingMap.get("ftpFilePath")).getSettingValue();
            if (StringUtils.isEmpty(this.ftpPath)) {
                this.ftpPath = ".";
            }

            this.ftpPath = this.ftpPath.replaceAll("\\\\", "/");
            this.channelSftpPool = new ChannelSftpPool(new ChannelSftpPoolConfig(), new ChannelSftpPooledObjectFactory(this.pubkeyAuthentication, this.idRsaPath, ftpServer, ftpAccount, ftpPassword, ftpPort));
        }
    }

    private void log() {
        if (this.channelSftpPool != null) {
            log.info("getMaxTotal = {},getNumActive = {},getNumIdle = {},createCount = {},borrowedCount = {},destroyedCount = {}", new Object[]{this.channelSftpPool.internalPool.getMaxTotal(), this.channelSftpPool.internalPool.getNumActive(), this.channelSftpPool.internalPool.getNumIdle(), this.channelSftpPool.internalPool.getCreatedCount(), this.channelSftpPool.internalPool.getBorrowedCount(), this.channelSftpPool.internalPool.getDestroyedCount()});
        }

    }

    public void rebuild() {
        this.isConfig = false;
        if (this.channelSftpPool != null) {
            this.channelSftpPool.close();
        }

        this.initSFTPConfig();
    }

    public ChannelSftp createSftp() {
        this.initSFTPConfig();
        return (ChannelSftp) this.channelSftpPool.getResource();
    }

    public void disconnect(ChannelSftp sftp) {
        if (sftp != null) {
            this.channelSftpPool.returnResourceObject(sftp);
        }

    }

    public boolean uploadFile(String fileName, InputStream inputStream) throws Exception {
        this.initSFTPConfig();
        ChannelSftp sftp = this.channelSftpPool.getResource();


        try {

            sftp.put(inputStream, this.getFileName(fileName));

        } catch (Exception var8) {
            log.error("Upload file failure. TargetPath: {}", fileName, var8);
            throw new Exception("Upload File failure");
        } finally {
            this.channelSftpPool.returnResourceObject(sftp);
        }

        return true;
    }
    public void copyFiles(Map<String,String> fileMap){
        this.initSFTPConfig();
        ChannelSftp sftp = this.channelSftpPool.getResource();
        fileMap.forEach((source,target)->{

            try {

                sftp.put(  this.getFileName(source),this.getFileName(target));
            } catch (SftpException e) {
                e.printStackTrace();

            }
        });

    }

    public void copyFile(String source,String target){
        this.initSFTPConfig();
        ChannelSftp sftp = this.channelSftpPool.getResource();
        try {
            sftp.put(  this.getFileName(source),this.getFileName(target));
        } catch (SftpException e) {
            e.printStackTrace();
        }
    }

    /**
     * "/data/temp"
     *
     * @throws SftpException
     */
    public void getRenameInfo(String path, List<Map<String, Object>> list) throws Exception {
        this.initSFTPConfig();
        ChannelSftp channelSftp = this.channelSftpPool.getResource();

        Vector vector = channelSftp.ls(path);


        for (Iterator<ChannelSftp.LsEntry> iterator = vector.iterator(); iterator.hasNext(); ) {
            ChannelSftp.LsEntry str = iterator.next();
            String filename = str.getFilename();
            if (filename.equals(".") || filename.equals("..")) {
                continue;
            }
            SftpATTRS attrs = str.getAttrs();
            boolean isDir = attrs.isDir();


            System.out.println("isDir====" + isDir);
            if (isDir) {
                getRenameInfo(path.concat("/").concat(filename), list);

            } else {
                String refId = UUID.randomUUID().toString().replace("-", "");
//

                String filePath = path.concat("/").concat(filename);

                String suffix = filename.substring(filename.indexOf(".") + 1);

                Map<String, Object> map = new HashMap<>();
                map.put("kcbh", filename.substring(0, filename.indexOf(".")));
                map.put("fileSize", Long.valueOf(attrs.getSize()).intValue());
                map.put("refId", refId);
                map.put("mimeType", MIMETYPE_SUFFIX.concat(suffix));
                map.put("suffix", suffix);

                map.put("filePath", filePath);

                list.add(map);
            }

        }


    }


    public void rename(List<Map<String, String>> list) {
        this.initSFTPConfig();
        ChannelSftp channelSftp = this.channelSftpPool.getResource();
        list.stream().forEach(map -> {
            try {
                channelSftp.rename(map.get("filePath"), map.get("toPath"));
            } catch (SftpException e) {
                e.printStackTrace();
            }
        });
    }

    /**
     * "/data/sftp/kcfm"
     *
     * @throws SftpException
     */
    public List<Map<String, Object>> getAllKcfmInfo(String path) throws Exception {
        this.initSFTPConfig();
        ChannelSftp channelSftp = this.channelSftpPool.getResource();
        Vector vector = channelSftp.ls(path);
        List<Map<String, Object>> attList = new ArrayList<Map<String, Object>>();
        for (Iterator<ChannelSftp.LsEntry> iterator = vector.iterator(); iterator.hasNext(); ) {
            ChannelSftp.LsEntry str = iterator.next();
            String filename = str.getFilename();
            if (filename.equals(".") || filename.equals("..")) {
                continue;
            }
            SftpATTRS attrs = str.getAttrs();
            boolean isDir = attrs.isDir();


            String name = filename.substring(0, filename.indexOf("."));
            System.out.println("isDir====" + isDir);
            if (!isDir) {
                Map<String, Object> att = new HashMap<>();
                String refId = UUID.randomUUID().toString().replace("-", "");
                att.put("fileSize", Long.valueOf(attrs.getSize()).intValue());
                att.put("refId", refId);
                att.put("mimeType", "image/jpg");
                att.put("name", filename);
                att.put("kcbh", name);
                attList.add(att);
            } else {
                log.info("目录，跳过");
            }

        }
        channelSftp.disconnect();
        return attList;
    }

    /**
     * "/data/sftp/kcfm"
     *
     * @throws SftpException
     */
    public Map<String, Object> getOneKcfmInfo(String path, String kcbh) throws Exception {
        this.initSFTPConfig();
        ChannelSftp channelSftp = this.channelSftpPool.getResource();
        Vector vector = channelSftp.ls(path);
        Map<String, Object> m = new HashMap<>();
        for (Iterator<ChannelSftp.LsEntry> iterator = vector.iterator(); iterator.hasNext(); ) {
            ChannelSftp.LsEntry str = iterator.next();
            String filename = str.getFilename();
            if (filename.equals(".") || filename.equals("..")) {
                continue;
            }
            SftpATTRS attrs = str.getAttrs();
            boolean isDir = attrs.isDir();


            String name = filename.substring(0, filename.indexOf("."));
            System.out.println("isDir====" + isDir);
            if (!isDir && name.equals(kcbh)) {
                String refId = UUID.randomUUID().toString().replace("-", "");
                m.put("fileSize", Long.valueOf(attrs.getSize()).intValue());
                m.put("refId", refId);
                m.put("mimeType", "image/jpg");
                m.put("name", filename);
                m.put("filename", name);
                return m;
            } else {
                //getRenameInfo(path.concat("/").concat(filename), list);
                log.info("目录，或图片与课程编号不匹配，跳过");
            }

        }
        channelSftp.disconnect();
        return m;
    }

    public void renameForOne(String filePath, String toPath) {
        this.initSFTPConfig();
        ChannelSftp channelSftp = this.channelSftpPool.getResource();
        try {
            log.info("课程封面将要复制到SFTP文件夹，从:{}到:{}", filePath, toPath);
            channelSftp.rename(filePath, toPath);
        } catch (SftpException e) {
            e.printStackTrace();
        }
    }

    public Map<String, File> batchDownloadFile(String filePath, List<String> fileNames) throws IOException, SftpException {
        this.initSFTPConfig();
        ChannelSftp sftp = (ChannelSftp) this.channelSftpPool.getResource();
        OutputStream outputStream = null;
        HashMap fileMap = new HashMap();

        try {
            Iterator var6 = fileNames.iterator();

            while (var6.hasNext()) {
                String fileName = (String) var6.next();

                try {
                    String targetPath = this.getFileName(fileName);
                    File file = new File(filePath + "/" + fileName);
                    if (!file.getParentFile().exists()) {
                        file.getParentFile().mkdirs();
                    }

                    outputStream = new FileOutputStream(file);
                    sftp.get(targetPath, outputStream);
                    log.info("Download file success. TargetPath: {}", targetPath);
                    fileMap.put(fileName, file);
                } finally {
                    if (outputStream != null) {
                        outputStream.close();
                    }

                }
            }
        } finally {
            this.channelSftpPool.returnResourceObject(sftp);
        }

        return fileMap;
    }


    public byte[] downloadFileToInputStream(String fileName) throws Exception {
        this.initSFTPConfig();
        ChannelSftp sftp = this.channelSftpPool.getResource();

        try {
            String fileName1 = getFileName(fileName);
            log.info("sftp pwd = {},fileName = {}", sftp.pwd(), fileName1);
            InputStream inputStream = sftp.get(fileName1);

            return toByteArray(inputStream);
        } catch (Exception var14) {
            log.error("Download file failure. TargetPath: " + fileName, var14);
            throw new Exception("Download File failure");
        } finally {
            try {
                this.channelSftpPool.returnResourceObject(sftp);
            } catch (Exception var13) {
                log.error(var13.getMessage(), var13);
            }
        }
    }

    public byte[] toByteArray(InputStream input) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buffer = new byte[4096];
        int n = 0;
        while (-1 != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
        }
        return output.toByteArray();
    }

    public String getFileName(String fileName) {
        if (this.ftpPath.endsWith("/")) {
            fileName = this.ftpPath + fileName;
        } else {
            fileName = this.ftpPath + "/" + fileName;
        }

        return fileName;
    }

    public Boolean checkSftp() {
        this.isConfig = false;
        if (this.channelSftpPool != null) {
            this.channelSftpPool.close();
        }

        this.initSFTPConfig();
        ChannelSftp sftp = null;

        Boolean var3;
        try {
            sftp = (ChannelSftp) this.channelSftpPool.getResource();
            sftp.cd(".");
            return true;
        } catch (Exception var7) {
            log.warn(var7.getMessage(), var7);
            var3 = false;
        } finally {
            this.channelSftpPool.returnResourceObject(sftp);
        }

        return var3;
    }

}
